/*******************************************************************************
GSight.cpp

	Author:		LiuDingXi
	e-mail:		liudx@gmail.com
	Date time:	2009-08-07

	Copyright (C) 2009
	All rights reserved.
	
*******************************************************************************/
#include "StdAfx.h"
#include "GScene.h"
#include "GTileMap.h"
#include "GSprite.h"
#include "GDialog.h"

#if (_OS_ == _OS_WINNT_)
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#endif//(_OS_ == _OS_WINNT_)

////////////////////////////////////////////////////////////////////////////////
GObject::GObject()
{
	m_nPosX = m_nPosY = 0;
	m_bVisible = true;
}

GObject::~GObject()
{
}

void GObject::SetPos(int x, int y)
{
	m_nPosX = x;
	m_nPosY = y;
}

bool GObject::IsVisible()const
{
	return m_bVisible;
}

bool GObject::SetVisible(bool bVisible)
{
	bool bOld = m_bVisible;
	m_bVisible = bVisible;
	return bOld;
}

//////////////////////////////////////////////////////////////////////////
//
// class GSight
//

GSight::GSight()
{
	m_pMaps = NULL;
	m_pSprites = NULL;

	m_pImages = NULL;
	m_pDialogs = NULL;
}

GSight::~GSight()
{
	Free();
}

bool GSight::Load(LPCTSTR szFilePath, LPTSTR pFile)
{
	if(szFilePath && pFile)
	{
		LPVOID pMapFile = MapFile(szFilePath);
		if(pMapFile != NULL)
		{
			const SIGHT_HEAD* pHead = (const SIGHT_HEAD*)pMapFile;

			if(pHead->dwMark == SIGHT_MARK)
			{
				if(pHead->nFiles > 0)
				{
					m_pImages = new GImage[pHead->nFiles];

					LPTSTR pSubfix = pFile;
					LPTSTR p = pSubfix;
					while(*p)
					{
						if(*p == _T('.'))
						{
							pSubfix = p;
						}
						p++;
					}

					for(int i=0; i<pHead->nFiles; i++)
					{
						p = pSubfix;

						//_stprintf(p, _T("%d.png"), i);
						// follow is same as _stprintf
						int v = i;
						TCHAR* pp = p + 1;
						do
						{
							*pp++ = _T('0') + (v % 10);
							v = v / 10;
						}while(v > 0);
						
						// reverse copy pp to p
						while(p < pp)
						{
							*p++ = *(--pp);
						}
						_tcscpy(p, _T(".png"));
						// end

						m_pImages[i].Load(szFilePath);
					}
				}

				const BYTE* pBase = (const BYTE*)pMapFile;

				if(pHead->nSprite > 0)
				{
					m_pSprites = new GSprite[pHead->nSprite];

					const PTR* pPtrs = (const PTR*)(pBase + pHead->pSprite);

					for(int i=0; i<pHead->nSprite; i++)
					{
						m_pSprites[i].DataBind(this, pBase, pPtrs[i]);
					}
				}
				if(pHead->nMap > 0)
				{
					m_pMaps = new GMap[pHead->nMap];

					const PTR* pPtrs = (const PTR*)(pBase + pHead->pMap);

					for(int i=0; i<pHead->nMap; i++)
					{
						m_pMaps[i].DataBind(this, pBase, pPtrs[i]);
					}
				}
				return true;
			}
			UnmapFile();
		}

	}
	return false;
}

void GSight::Free()
{
	while(m_pDialogs)
	{
		GDialog* pDialog = m_pDialogs;
		m_pDialogs = pDialog->m_pNext;
		pDialog->Destroy();
	}

	delete[] m_pMaps;
	m_pMaps = NULL;

	delete[] m_pSprites;
	m_pSprites = NULL;

	delete[] m_pImages;
	m_pImages = NULL;

	UnmapFile();
}

bool GSight::HasLoaded()const
{
	return (m_pMapFile == NULL ? false : true);
}

bool GSight::HasInited()const
{
	if(m_pMapFile)
	{
		if(m_pMaps)
		{
			const SIGHT_HEAD* pHead = (const SIGHT_HEAD*)m_pMapFile;
			for(int i = 0; i< pHead->nMap; i++)
			{
				if(!m_pMaps[i].IsValid())
					return false;
			}
		}
		return true;
	}
	return false;
}

int GSight::GetDialogCount()const
{
	if(m_pMapFile)
	{
		const SIGHT_HEAD* pHead = (const SIGHT_HEAD*)m_pMapFile;
		return pHead->nDialog;
	}
	return 0;
}

int GSight::GetMapCount()const
{
	if(m_pMapFile)
	{
		const SIGHT_HEAD* pHead = (const SIGHT_HEAD*)m_pMapFile;
		return pHead->nMap;
	}
	return 0;
}

int GSight::GetSpriteCount()const
{
	if(m_pMapFile)
	{
		const SIGHT_HEAD* pHead = (const SIGHT_HEAD*)m_pMapFile;
		return pHead->nSprite;
	}
	return 0;
}

GImage* GSight::GetImage(int nId)const
{
	if(m_pMapFile && m_pImages)
	{
		const SIGHT_HEAD* pHead = (const SIGHT_HEAD*)m_pMapFile;
		if(nId >= 0 && nId < pHead->nFiles)
		{
			return (m_pImages + nId);
		}
	}
	return NULL;
}

GPStyle GSight::GetStyle(int nId)const
{
	if(m_pMapFile)
	{
		const SIGHT_HEAD* pHead = (const SIGHT_HEAD*)m_pMapFile;
		if(nId >= 0 && nId < pHead->nStyle)
		{
			const BYTE* pBase = (const BYTE*)m_pMapFile;
			const STYLE* pStyles = (const STYLE*)(pBase + pHead->pStyle);
			return (pStyles + nId);
		}
	}
	return NULL;
}

BOOL GSight::Init(LPCTSTR szFolder, GDC* pDC)
{
	if(m_pMaps)
	{
		const SIGHT_HEAD* pHead = (const SIGHT_HEAD*)m_pMapFile;

		for(int i=0; i<pHead->nMap; i++)
		{
			m_pMaps[i].Init(szFolder, pDC);
		}
	}
	return TRUE;
}


BOOL GSight::InitDialog(GDialog* pDialog, UINT nID)
{
	if(m_pMapFile && pDialog && nID >= 0)
	{
		const SIGHT_HEAD* pHead = (const SIGHT_HEAD*)m_pMapFile;
		if(nID < pHead->nDialog)
		{
			ASSERT(pDialog->m_pNext == NULL);
			pDialog->m_pNext = m_pDialogs;
			m_pDialogs = pDialog;

			const BYTE* pBase = (const BYTE*)m_pMapFile;

			const PTR* pPtrs = (const PTR*)(pBase + pHead->pDialog);
			return pDialog->DataBind(this, pBase, pPtrs[nID]);
		}
	}
	return FALSE;
}


//////////////////////////////////////////////////////////////////////////
//
// class GScene
//

GScene::GScene()
{
	m_pEngine = NULL;

	m_pHead = NULL;
	m_pTail = NULL;
	m_pTemp = NULL;

	m_nUnload = 0;
	m_nUninit = 0;
}

GScene::~GScene()
{
	Free();
}

BOOL GScene::Init(LPCTSTR szFolder, GDC* pDC, GEngine* pEngine)
{
	m_pEngine = pEngine;

	InitSights(0, szFolder, pDC);
	return TRUE;
}

bool GScene::Load(LPCTSTR szFilePath, LPTSTR pFile)
{
	if(szFilePath && pFile)
	{
		LPVOID pMapFile = MapFile(szFilePath);
		if(pMapFile != NULL)
		{
			const SCENE_HEAD* pHead = (const SCENE_HEAD*)pMapFile;

			if(pHead->dwMark == SCENE_MARK)
			{
				m_nUnload = pHead->nSights;
				m_nUninit = m_nUnload;
				m_pSights = new GSight[m_nUnload];
				if(m_pSights)
				{
					LoadSights(0, szFilePath, pFile);
					return true;
				}
			}
			UnmapFile();
		}
	}
	return false;
}

void GScene::Free()
{
	m_pTail = NULL;
	while(m_pHead)
	{
		VNODE* pNode = m_pHead;
		m_pHead = pNode->pNext;
		delete pNode;
	}
	while(m_pTemp)
	{
		VNODE* pNode = m_pTemp;
		m_pTemp = pNode->pNext;
		delete pNode;
	}

	delete[] m_pSights;
	m_pSights = NULL;

	UnmapFile();
}

BOOL GScene::NeedLoadMore()const
{
	if(m_nUnload || m_nUninit)
		return TRUE;
	return FALSE;
}

SINT GScene::LoadSights(SINT nDelay, LPCTSTR szFilePath, LPTSTR pFile)
{
	if(m_nUnload > 0 && m_pMapFile && m_pSights)
	{
		const BYTE* pBase = (const BYTE*)m_pMapFile;
		const SIGHT_LOAD* pSload = (const SIGHT_LOAD*)(pBase + sizeof(SCENE_HEAD));
		
		const SCENE_HEAD* pHead = (const SCENE_HEAD*)m_pMapFile;
		for(SINT i=0; i< pHead->nSights; i++)
		{
			if(pSload[i].nDelay <= nDelay && !m_pSights[i].HasLoaded())
			{
				const SceneDat::CHAR* pFileName = 
					(const SceneDat::CHAR*)(pBase + pSload[i].pSight);

				_tcscpyA2T(pFile, pFileName);
				if(!m_pSights[i].Load(szFilePath, pFile))
				{
					return -1;
				}
				m_nUnload--;
			}
		}
	}
	return m_nUnload;
}

SINT GScene::InitSights(SINT nDelay, LPCTSTR szFolder, GDC* pDC)
{
	if(m_nUninit > 0 && m_pMapFile && m_pSights)
	{
		const BYTE* pBase = (const BYTE*)m_pMapFile;
		const SIGHT_LOAD* pSload = (const SIGHT_LOAD*)(pBase + sizeof(SCENE_HEAD));
		
		const SCENE_HEAD* pHead = (const SCENE_HEAD*)m_pMapFile;
		SINT nInit = 0;
		for(SINT i=0; i< pHead->nSights; i++)
		{
			if(pSload[i].nDelay <= nDelay && !m_pSights[i].HasInited())
			{
				if(!m_pSights[i].Init(szFolder, pDC))
				{
					return -1;
				}
			}
			if(m_pSights[i].HasInited())
			{
				nInit++;
			}
		}
		m_nUninit = pHead->nSights - nInit;
	}
	return m_nUninit;
}


bool GScene::IsVisible()const
{
	return true;
}

BOOL GScene::CheckReady(SINT nCount, const SINT* pItems)const
{
	const SCENE_HEAD* pHead = (const SCENE_HEAD*)m_pMapFile;
	if(pItems && nCount > 0 && m_pSights && pHead)
	{
		for(SINT i=0; i<nCount; i++)
		{
			SINT iSight = pItems[i] >> 16;
			if(iSight >= 0 && iSight < pHead->nSights)
			{
				GSight* pSight = m_pSights + iSight;
				if( !pSight->HasLoaded() ||
					!pSight->HasInited() )
					return FALSE;
			}
		}
		return TRUE;
	}
	return FALSE;
}

GMap* GScene::GetMap(SINT iMap)
{
	const SCENE_HEAD* pHead = (const SCENE_HEAD*)m_pMapFile;
	if(pHead && m_pSights)
	{
		SINT iSight = iMap >> 16;
		if(iSight >= 0 && iSight < pHead->nSights)
		{
			iMap = iMap & 0x0000FFFF;
			GSight* pSight = m_pSights + iSight;
			if(iMap >= 0 && iMap < pSight->GetMapCount())
			{
				return pSight->m_pMaps + iMap;
			}
		}
	}
	return NULL;
}

GSprite* GScene::GetSprite(SINT iSprite)
{
	const SCENE_HEAD* pHead = (const SCENE_HEAD*)m_pMapFile;
	if(pHead && m_pSights)
	{
		SINT iSight = iSprite >> 16;
		if(iSight >= 0 && iSight < pHead->nSights)
		{
			iSprite = iSprite & 0x0000FFFF;
			GSight* pSight = m_pSights + iSight;
			if(iSprite >= 0 && iSprite < pSight->GetSpriteCount())
			{
				return pSight->m_pSprites + iSprite;
			}
		}
	}
	return NULL;
}

BOOL GScene::InitDialog(GDialog* pDialog, UINT nID)
{
	const SCENE_HEAD* pHead = (const SCENE_HEAD*)m_pMapFile;
	if(pHead && m_pSights)
	{
		SINT iSight = nID >> 16;
		if(iSight >= 0 && iSight < pHead->nSights)
		{
			nID = nID & 0x0000FFFF;
			GSight* pSight = m_pSights + iSight;
			return pSight->InitDialog(pDialog, nID);
		}
	}
	return FALSE;
}

void GScene::OnDraw(GDC* pDC)
{
	VNODE* pNode = m_pHead;
	while(pNode)
	{
		if(pNode->pObject && pNode->pObject->IsVisible())
		{
			pNode->pObject->OnDraw(pDC);
		}
		pNode = pNode->pNext;
	}
}

LONG GScene::Update(UINT nMillisecond)
{
	VNODE* pNode = m_pHead;
	while(pNode)
	{
		if(pNode->pObject)
		{
			pNode->pObject->Update(nMillisecond);
		}
		pNode = pNode->pNext;
	}
	return 0;
}

PMRV GScene::ProcMsg(GMsg& refMsg)
{
	VNODE* pNode = m_pTail;
	while(pNode)
	{
		if(pNode->pObject && pNode->pObject->IsVisible())
		{
			PMRV ret = pNode->pObject->ProcMsg(refMsg);
			if(ret == PM_HOLD)
			{
				return PM_HOLD;
			}
			if(pNode->nType == eDialog)
			{
				return PM_HOLD;
			}
		}
		pNode = pNode->pPrev;
	}

	return PM_PASS;
}

GScene::VNODE* GScene::AddHead(GObject* pObject, UINT nType)
{
	VNODE*	pNode = m_pTemp;
	if(m_pTemp)
	{
		m_pTemp = m_pTemp->pNext;
	}
	else
	{
		pNode = new VNODE;
	}
	if(pNode)
	{
		pNode->pObject = pObject;
		pNode->nType = nType;
		pNode->pPrev = NULL;
		pNode->pNext = m_pHead;
		if(m_pHead)
		{
			m_pHead->pPrev = pNode;
		}
		else
		{
			m_pTail = pNode;
		}
		m_pHead = pNode;
	}
	return pNode;
}


GScene::VNODE* GScene::AddTail(GObject* pObject, UINT nType)
{
	VNODE*	pNode = m_pTemp;
	if(m_pTemp)
	{
		m_pTemp = m_pTemp->pNext;
	}
	else
	{
		pNode = new VNODE;
	}
	if(pNode)
	{
		pNode->pObject = pObject;
		pNode->nType = nType;
		pNode->pNext = NULL;
		pNode->pPrev = m_pTail;
		if(m_pTail)
		{
			m_pTail->pNext = pNode;
		}
		else
		{
			m_pHead = pNode;
		}
		m_pTail = pNode;
	}
	return pNode;
}

GScene::VNODE* GScene::Find(GObject* pObject, BOOL bHead)
{
	VNODE*	pNode = NULL;
	if(bHead)
	{
		pNode = m_pHead;
		while(pNode)
		{
			if(pNode->pObject == pObject)
			{
				break;
			}
			pNode = pNode->pNext;
		}
	}
	else
	{
		pNode = m_pTail;
		while(pNode)
		{
			if(pNode->pObject == pObject)
			{
				break;
			}
			pNode = pNode->pPrev;
		}
	}
	return pNode;
}

GObject* GScene::Remove(GScene::VNODE* pNode)
{
	if(pNode->pPrev)
	{
		if(pNode->pNext)
		{
			pNode->pNext->pPrev = pNode->pPrev;
			pNode->pPrev->pNext = pNode->pNext;
		}
		else
		{
			ASSERT(m_pTail == pNode);
			m_pTail = pNode->pPrev;
			m_pTail->pNext = NULL;
		}
	}
	else
	{
		ASSERT(m_pHead == pNode);
		m_pHead = pNode->pNext;
		if(m_pHead)
		{
			m_pHead->pPrev = NULL;
		}
	}
	pNode->pNext = m_pTemp;
	m_pTemp = pNode;
	return pNode->pObject;
}

GScene::VNODE* GScene::RemoveHead(GObject* pObject)
{
	VNODE* pNode = m_pHead;
	while(pNode)
	{
		if(pNode->pObject == pObject)
		{
			Remove(pNode);
			return pNode;
		}
		pNode = pNode->pNext;
	}
	return NULL;
}

GScene::VNODE* GScene::RemoveTail(GObject* pObject)
{
	VNODE* pNode = m_pTail;
	while(pNode)
	{
		if(pNode->pObject == pObject)
		{
			Remove(pNode);
			return pNode;
		}
		pNode = pNode->pPrev;
	}
	return NULL;
}
